// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/builtins/growable-fixed-array-gen.h"

#include "src/compiler/code-assembler.h"

#include "src/objects-inl.h" // weolar

namespace v8 {
namespace internal {

    void GrowableFixedArray::Push(TNode<Object> const value)
    {
        TNode<IntPtrT> const length = var_length_.value();
        TNode<IntPtrT> const capacity = var_capacity_.value();

        Label grow(this), store(this);
        Branch(IntPtrEqual(capacity, length), &grow, &store);

        BIND(&grow);
        {
            var_capacity_ = NewCapacity(capacity);
            var_array_ = ResizeFixedArray(length, var_capacity_.value());

            Goto(&store);
        }

        BIND(&store);
        {
            TNode<FixedArray> const array = var_array_.value();
            UnsafeStoreFixedArrayElement(array, length, value);

            var_length_ = IntPtrAdd(length, IntPtrConstant(1));
        }
    }

    TNode<JSArray> GrowableFixedArray::ToJSArray(TNode<Context> const context)
    {
        const ElementsKind kind = PACKED_ELEMENTS;

        TNode<Context> const native_context = LoadNativeContext(context);
        TNode<Map> const array_map = LoadJSArrayElementsMap(kind, native_context);

        // Shrink to fit if necessary.
        {
            Label next(this);

            TNode<IntPtrT> const length = var_length_.value();
            TNode<IntPtrT> const capacity = var_capacity_.value();

            GotoIf(WordEqual(length, capacity), &next);

            var_array_ = ResizeFixedArray(length, length);
            var_capacity_ = length;
            Goto(&next);

            BIND(&next);
        }

        TNode<Smi> const result_length = SmiTag(length());
        TNode<JSArray> const result = AllocateJSArray(array_map, var_array_.value(), result_length);
        return result;
    }

    TNode<IntPtrT> GrowableFixedArray::NewCapacity(
        TNode<IntPtrT> current_capacity)
    {
        CSA_ASSERT(this,
            IntPtrGreaterThanOrEqual(current_capacity, IntPtrConstant(0)));

        // Growth rate is analog to JSObject::NewElementsCapacity:
        // new_capacity = (current_capacity + (current_capacity >> 1)) + 16.

        TNode<IntPtrT> const new_capacity = IntPtrAdd(IntPtrAdd(current_capacity, WordShr(current_capacity, 1)),
            IntPtrConstant(16));

        return new_capacity;
    }

    TNode<FixedArray> GrowableFixedArray::ResizeFixedArray(
        TNode<IntPtrT> const element_count, TNode<IntPtrT> const new_capacity)
    {
        CSA_ASSERT(this, IntPtrGreaterThanOrEqual(element_count, IntPtrConstant(0)));
        CSA_ASSERT(this, IntPtrGreaterThanOrEqual(new_capacity, IntPtrConstant(0)));
        CSA_ASSERT(this, IntPtrGreaterThanOrEqual(new_capacity, element_count));

        TNode<FixedArray> const from_array = var_array_.value();

        CodeStubAssembler::ExtractFixedArrayFlags flags;
        flags |= CodeStubAssembler::ExtractFixedArrayFlag::kFixedArrays;
        TNode<FixedArray> to_array = CAST(ExtractFixedArray(
            from_array, nullptr, element_count, new_capacity, flags));

        return to_array;
    }

} // namespace internal
} // namespace v8
